package com.scf.gateway.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.scf.uitl.JwtUtil;
import com.scf.uitl.ResponseResult;
import com.scf.uitl.ResultStatus;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

/**
 * @author author
 * @create 2021-11-11-15:03
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    //注入redis
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    //执行过滤方法，与zuul的run方法作用一致
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取request  此request非彼request
        ServerHttpRequest request= exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取uri
        String path = request.getURI().getPath();



        //判断uri是否需要认证才能访问
        if(validate(path)){
            System.out.println(path);
            return chain.filter(exchange);
        }
        //执行到此处，说明上面的if不成立，表示需要认证
        //获取头信息
        //String header = request.getHeaders().get("authentication").get(0);
        List<String> auth = request.getHeaders().get("authentication");
        String token=null;
        if(auth!=null){
            token=auth.get(0);
        }
        List<String> refreshList = request.getHeaders().get("refresh");

        String refresh=null;
        if(refreshList!=null){
            refresh=refreshList.get(0);
        }

        //判断token、refresh是否为null
        if(token.equals("null") || refresh.equals("null")){
            //没有携带token、refresh  说明没有登陆，直接结束，返回登陆结果
            return result(response);
        }
        System.out.println(refresh);
        //执行到此处说明token、refresh都不为空，只能说明携带了，还需要对他们进行校验
        //校验refresh：判断redis中是否有这个key，没有表示refresh伪造或者过期
        if(!redisTemplate.hasKey(refresh)){

            //不含refresh
            return result(response);
        }
        Object uname = redisTemplate.opsForHash().get(refresh, "uname");
        System.out.println(uname);
        switch (JwtUtil.verify(token)){
            case TOKEN_BAD:
                return result(response);
            case TOKEN_EXPIRE:
                //重新生成token，并返回给浏览器
                token=JwtUtil.generateToken(JwtUtil.getUname(token));
                //将新token设置到响应头
                response.getHeaders().add("authentication", token);
                response.getHeaders().add("Access-Control-Expose-Headers", "authentication");
                redisTemplate.opsForHash().put(refresh, "token", token);
                break;
        }

        System.out.println("gateway的过滤器执行过滤");
        //System.out.println(header);
        //如果登陆，放行

        //继续向下执行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private Mono<Void> result(ServerHttpResponse response){
        //假设当前用户需要认证，而且没有认证，应该给前端返回需要认证的结果 JSON
        ResponseResult responseResult=new ResponseResult();
        responseResult.setCode(201);
        responseResult.setMessage("请登录");
        responseResult.setStatus(ResultStatus.NO_LOGIN);

        //需要将返回的对象转换成byte类型的数组
        byte[] data=null;

        try {
            data=new ObjectMapper().writeValueAsString(responseResult).getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //创建buffer对象
        DataBuffer dataBuffer=response.bufferFactory().wrap(data);

        //设置响应头
        response.getHeaders().add("Content-Type", "application/json;charset=utf-8");

        //返回数据
        return response.writeWith(Mono.just(dataBuffer));


    }
    //校验uri是否需要进行认证才能访问
    private boolean validate(String uri){
        List<String> anon= Arrays.asList(
                "/auth",
                "/file"
//                "/auth",
//                "/user"
        );
        for (String str:anon){
            if(uri.startsWith(str)){
                return true;
            }
        }
        return false;
    }
}
